home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / MPW-GM / MPW / Examples / CFMExamples / ModApp / ModuleSources / Clock.c < prev    next >
Encoding:
Text File  |  1998-12-03  |  11.4 KB  |  431 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Clock.c
  3.  
  4.     Contains:    A simple analog clock
  5.  
  6.     Written by:    Richard Clark
  7.  
  8.     Copyright:    © 1994 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.                  8/15/94    BLS        Updated for CFM-68K.  Return ProcPtrs instead of UPPs.
  13.  
  14.                  2/16/94    RC        Added code in ToolIdle() to see if the GWorld has been
  15.                                      purged.
  16.                  1/27/94    RC        Changed initialization code so that not getting a buffer isn't
  17.                                      fatal anymore.
  18.                  1/14/94    RC        Added code to set the fg color to black before drawing the frame
  19.  
  20.     To Do:
  21. */
  22.  
  23. // File: ClockTool.c
  24.  
  25. #ifdef THINK_C
  26.     #define ToolStartup main
  27. #endif
  28.  
  29. #ifndef __MEMORY__
  30.     #include <Memory.h>
  31. #endif
  32.  
  33. #ifndef __RESOURCES__
  34.     #include <Resources.h>
  35. #endif
  36.  
  37. #ifndef __MENUS__
  38.     #include <Menus.h>
  39. #endif
  40.  
  41. #ifndef __OSUTILS__
  42.     #include <OSUtils.h>    // For Secs2Date, used in our Idle routine
  43. #endif
  44.  
  45. #ifndef __FIXMATH__
  46.     #include <FixMath.h>    // For fixed-point math routines, used in drawing the clock face
  47. #endif
  48.  
  49. #ifndef __TOOLUTILS__
  50.     #include <ToolUtils.h>    // For fixed-point math routines, used in drawing the clock face
  51. #endif
  52.  
  53. #include "GWorldTools.h"
  54. #include "ToolAPI.h"
  55. #include "ModApp.h"
  56.  
  57. struct ToolPrivateData {
  58.     GWorldPtr        buffer;
  59.     unsigned long    lastUpdate;        // What time did we last update the clock (seconds)
  60.     Rect            bodyRect;        // The bounds of our clock face
  61.     Point            centerPt;        // The center of our clock face
  62.     Boolean            showSeconds;
  63.     RGBColor        hourColor;
  64.     RGBColor        minuteColor;
  65.     RGBColor        secondColor;
  66. };
  67.  
  68. typedef struct ToolPrivateData ToolData, *ToolDataPtr;
  69.  
  70. enum {
  71.     // Menu information
  72.     kToolMenu = 2001,
  73.     // --- Commands
  74.     kShowSeconds = 1
  75. };
  76.  
  77. // === Prototypes for utility routines (at the end of the file)
  78. static Rect GetBodyRect (Rect thePortRect);
  79. static void AngleToPoint (short angle, short radius, Point center, Point *result);
  80. static void PaintHand (short baseAngle, short length, short decorAngle, short decorLength, Point centerPt);
  81.  
  82. // === Public routines
  83.  
  84. OSErr ToolStartup (WindowPtr wp)
  85. {
  86.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  87.     OSErr                err = noErr;
  88.     ToolDataPtr            privateData = NULL;
  89.     Rect                bodyRect, globalBounds;
  90.     MenuHandle            privateMenu;
  91.     short                refNum;
  92.     
  93.     aWindow->toolRoutines.shutdownProc = ToolShutdown;
  94.     aWindow->toolRoutines.menuAdjustProc = ToolMenuAdjust;
  95.     aWindow->toolRoutines.menuDispatchProc = ToolMenuDispatch;
  96.     aWindow->toolRoutines.toolIdleProc = ToolIdle;
  97.     aWindow->toolRoutines.toolUpdateProc = ToolUpdate;
  98.     aWindow->toolRoutines.toolClickProc = ToolWindowClick;
  99.     aWindow->toolRoutines.toolWindowMovedProc = ToolWindowMoved;
  100.     aWindow->toolRoutines.toolWindowResizedProc = ToolWindowResized;
  101.     aWindow->toolRoutines.toolWindowActivateProc = ToolWindowActivate;
  102.  
  103.     // Allocate our private storage
  104.     privateData = (ToolDataPtr)NewPtrClear(sizeof(ToolData));
  105.     err = MemError();
  106.     if (err) goto error;
  107.  
  108.     // Allocate an offscreen buffer
  109.     globalBounds = GetGlobalBounds(wp);
  110.     err = AllocateBuffer (wp, globalBounds, &privateData->buffer, false);
  111.     // Don't abort on an error, since that just means we didn't get a buffer
  112.     
  113.     // Set other local variables
  114.     bodyRect = GetBodyRect(wp->portRect);
  115.     privateData->bodyRect = bodyRect;
  116.     privateData->centerPt.h = (bodyRect.left + bodyRect.right) / 2;
  117.     privateData->centerPt.v = (bodyRect.top + bodyRect.bottom) / 2;
  118.     privateData->showSeconds = true;
  119.     privateData->lastUpdate = 0;
  120.     
  121.     privateData->hourColor.red = 0x0000;
  122.     privateData->hourColor.green = 0x5A00;
  123.     privateData->hourColor.blue = 0xFFFF;
  124.     
  125.     privateData->minuteColor.red = 0x0B00;
  126.     privateData->minuteColor.green = 0x0B00;
  127.     privateData->minuteColor.blue = 0xFFFF;
  128.     
  129.     privateData->secondColor.red = 0xFFFF;
  130.     privateData->secondColor.green = 0x0000;
  131.     privateData->secondColor.blue = 0x0000;
  132.     
  133.     // Add our menu
  134.     refNum = FSpOpenResFile(&aWindow->toolSpec, fsCurPerm);
  135.     err = ResError();
  136.     if (err) goto error;
  137.     privateMenu = GetMenu(kToolMenu);
  138.     DetachResource((Handle)privateMenu);
  139.     InsertMenu(privateMenu, 0);
  140.     DrawMenuBar();
  141.     CloseResFile(refNum);
  142.     
  143.     goto noError;
  144.  
  145. error:
  146.     if (privateData) {
  147.         if (privateData->buffer)
  148.             DisposeBuffer(&privateData->buffer);
  149.         DisposePtr((Ptr)privateData);
  150.     }
  151.     return err;
  152.  
  153. noError:
  154.     aWindow->toolRefCon = (long)privateData;
  155.     return noErr;
  156. }
  157.  
  158.  
  159. void ToolShutdown (WindowPtr wp)
  160. {
  161.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  162.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  163.     
  164.     if (privateData) {
  165.         if (privateData->buffer)
  166.             DisposeBuffer(&privateData->buffer);
  167.         DisposePtr((Ptr)privateData);
  168.     }
  169.  
  170. }
  171.  
  172.  
  173. void ToolMenuAdjust (WindowPtr wp)
  174. {
  175.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  176.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  177.  
  178.     CheckItem(GetMenuHandle(kToolMenu), kShowSeconds, privateData->showSeconds);
  179. }
  180.  
  181.  
  182. void ToolMenuDispatch (WindowPtr wp, short menuID, short itemID)
  183. {
  184.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  185.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  186.     
  187.     if ((menuID == kToolMenu) && (itemID == kShowSeconds))
  188.             privateData->showSeconds = !privateData->showSeconds;
  189. }
  190.  
  191.  
  192. void ToolIdle (WindowPtr wp)
  193. {
  194.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  195.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  196.     OSErr                bufError;
  197.     unsigned long        currTime;
  198.     DateTimeRec            now;
  199.     CGrafPtr            oldPort;
  200.     GDHandle            oldDevice;
  201.     GWorldFlags         oldPixState;
  202.  
  203.     // Values used in drawing the clock face
  204.     short                hour;
  205.     short                radius, handLength;
  206.     Point                startPt, endPt;
  207.     Point                centerPt;
  208.  
  209.     GetDateTime(&currTime);
  210.     if (currTime > privateData->lastUpdate) {    // <<< Need to update less frequently if the second hand is off
  211.  
  212.         if (privateData->buffer != NULL) {
  213.             bufError = LockBuffer (privateData->buffer, &oldPixState);
  214.             // Was our buffer purged?
  215.             if (bufError == kPixelsPurged) {
  216.                 // try to reallocate and lock the buffer
  217.                 OSErr err2;
  218.                 err2 = UpdateBuffer(wp, &privateData->buffer);
  219.                 if (err2 == noErr) 
  220.                     bufError = LockBuffer (privateData->buffer, &oldPixState);
  221.             }
  222.             // An error should only happen if the buffer couldn't be locked down
  223.             // or was purged and cannot be reallocated. In that case, we'll draw
  224.             // without buffering
  225.             if (bufError == noErr) {
  226.                 GetGWorld(&oldPort,&oldDevice);
  227.                 SetGWorld(privateData->buffer, NULL);
  228.             }
  229.         }
  230.         
  231.         // Draw, podnah
  232.         EraseRect(&wp->portRect);
  233.         privateData->lastUpdate = currTime;
  234.         SecondsToDate(currTime, &now);
  235.         radius = privateData->centerPt.h - privateData->bodyRect.left;
  236.         centerPt = privateData->centerPt;
  237.         
  238.         // Draw the clock face
  239.         ForeColor(blackColor);
  240.         FrameOval(&privateData->bodyRect);
  241.         // Draw the tick marks
  242.         for (hour = 0; hour <= 11; hour++) {
  243.             AngleToPoint(hour * 30, radius - 1, centerPt, &startPt);
  244.             AngleToPoint(hour * 30, radius - 5, centerPt, &endPt);
  245.             MoveTo(startPt.h, startPt.v);
  246.             LineTo(endPt.h, endPt.v);
  247.         }
  248.             
  249.         // draw the hour hand
  250.         RGBForeColor(&privateData->hourColor);
  251.         handLength = radius / 2;
  252.         if (now.hour >= 12) now.hour -= 12;
  253.         PaintHand((now.hour * 30) + (now.minute / 2), handLength, 120, radius / 16, centerPt);    // hour hand
  254.         
  255.         // Draw the minute hand
  256.         
  257.         RGBForeColor(&privateData->minuteColor);
  258.         handLength = (radius * 7) / 8;
  259.         PaintHand(now.minute * 6, handLength, 120, radius / 16, centerPt);    // hour hand
  260.         
  261.         // Draw the second hand
  262.         if (privateData->showSeconds) {
  263.             RGBForeColor(&privateData->secondColor);
  264.             PaintHand(now.second * 6, radius - 5, 0, 0, centerPt);    // second hand
  265.             ForeColor(blackColor);
  266.         }
  267.         
  268.         // Now, take the buffered image and transfer it to the screen
  269.         if ((privateData->buffer != NULL) && (bufError == noErr)) {
  270.             UnlockBuffer(privateData->buffer, oldPixState);
  271.             SetGWorld(oldPort, oldDevice);
  272.             CopyBufferToWindow (wp, privateData->buffer);
  273.         }
  274.     }
  275. }
  276.  
  277.  
  278. void ToolUpdate (WindowPtr wp)
  279. {
  280.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  281.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  282.     OSErr                err;
  283.     
  284.     if (privateData->buffer) {
  285.         err = CopyBufferToWindow (wp, privateData->buffer);
  286.         if (err)
  287.         // Something went wrong with the buffer, so use our idle routine to redraw the
  288.         // window
  289.             ToolIdle(wp);
  290.     } else
  291.         ToolIdle(wp);
  292. }
  293.  
  294.  
  295. void ToolWindowClick(WindowPtr wp, EventRecord *theEvent)
  296. {
  297.     // Do nothing!
  298.     #pragma unused(wp, theEvent)
  299. }
  300.  
  301.  
  302. void ToolWindowMoved(WindowPtr wp)
  303. {
  304.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  305.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  306.  
  307.     (void)UpdateBuffer(wp, &privateData->buffer);
  308.     privateData->lastUpdate = 0; // Force an update
  309.     ToolIdle(wp);
  310. }
  311.  
  312.  
  313. void ToolWindowResized(WindowPtr wp)
  314. // Update the offscreen gWorld and our local bounds rect to match the
  315. // new window size.
  316. {
  317.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  318.     ToolDataPtr            privateData = (ToolDataPtr)aWindow->toolRefCon;
  319.     OSErr                err;
  320.     Rect                bodyRect;
  321.     Rect                globalBounds;
  322.     
  323.     // Calculate the new body rect
  324.     bodyRect = GetBodyRect(wp->portRect);
  325.     privateData->bodyRect = bodyRect;
  326.     privateData->centerPt.h = (bodyRect.left + bodyRect.right) / 2;
  327.     privateData->centerPt.v = (bodyRect.top + bodyRect.bottom) / 2;
  328.  
  329.     // Create the new off-screen buffer
  330.     globalBounds = GetGlobalBounds(wp);
  331.     err = AllocateBuffer (wp, globalBounds, &privateData->buffer, true); // Delete the old buffer & create a new one
  332.     if (err == noErr) {
  333.         // Force a redraw
  334.         privateData->lastUpdate = 0; // Force an update
  335.         ToolIdle(wp);
  336.     }
  337. }
  338.  
  339.  
  340. void ToolWindowActivate(WindowPtr wp, Boolean activeFlag)
  341. {
  342.     // Nothing special to do
  343.     #pragma unused(wp, activeFlag)
  344. }
  345.  
  346.  
  347. // === Private routines
  348. // Private routine to calculate the rectangle which will hold our clock face
  349. static Rect GetBodyRect (Rect thePortRect)
  350. {
  351.     short    height, width;
  352.     short    side;
  353.     Rect    bodyRect;
  354.  
  355.     // Find the smaller side of the port rectangle and use that
  356.     // as the size of our rectangle
  357.     height = thePortRect.bottom - thePortRect.top;
  358.     width = thePortRect.right - thePortRect.left;
  359.     if (height < width)
  360.         side = height;
  361.     else
  362.         side = width;
  363.     // Create the new rectangle, centered
  364.     side /= 2;
  365.     SetRect(&bodyRect, -side, -side, side, side);
  366.     OffsetRect(&bodyRect, (thePortRect.right + thePortRect.left) / 2, (thePortRect.bottom + thePortRect.top) / 2);
  367.     InsetRect(&bodyRect, 8, 8);
  368.     return bodyRect;
  369. }
  370.  
  371. enum {
  372.     pi = 0x32439
  373.     };
  374.  
  375.  
  376. static Fixed DecToRad (short angle)
  377. {
  378.     return FixDiv(FixMul(angle, pi), 180L);
  379. }
  380.  
  381. #define FixedToShort(x) ((x >> 16) & 0x0000FFFF)
  382. #define ShortToFixed(x) ((long)x << 16)
  383.  
  384. void AngleToPoint (short angle, short radius, Point center, Point *result)
  385. // Given an angle, and a distance from a central point, find the resulting point
  386. // N.B. 0 is straight up
  387. {    
  388.     register Fixed fixedRadius = ShortToFixed(radius);
  389.     
  390.     // First, bring the angle into the range 0..360
  391.     while (angle > 360) angle -= 360;
  392.     while (angle < 0) angle += 360;
  393.     
  394.     result->h = center.h + FixedToShort(FracMul(fixedRadius, FracSin(DecToRad(angle))));
  395.     result->v = center.v - FixedToShort(FracMul(fixedRadius, FracCos(DecToRad(angle))));
  396. }
  397.  
  398.  
  399. static void PaintHand (short baseAngle, short length, short decorAngle, short decorLength, Point centerPt)
  400. {
  401.     PolyHandle        scratchPoly;
  402.     Point            nextPt;
  403.  
  404.  
  405.     MoveTo(centerPt.h, centerPt.v);
  406.  
  407.     // Draw the right-hand side of the base
  408.     if (decorAngle) {
  409.         // Use a polygon to build the image if the width is > 1
  410.         scratchPoly = OpenPoly();
  411.         AngleToPoint(baseAngle + decorAngle, decorLength, centerPt, &nextPt);
  412.         LineTo(nextPt.h, nextPt.v);
  413.     }
  414.  
  415.     // Out to the end of the hand
  416.     AngleToPoint(baseAngle, length, centerPt, &nextPt);
  417.     LineTo(nextPt.h, nextPt.v);
  418.  
  419.     // Down the other side...
  420.     if (decorAngle) {
  421.         AngleToPoint(baseAngle - decorAngle, decorLength, centerPt, &nextPt);
  422.         LineTo(nextPt.h, nextPt.v);
  423.         // ...and back home
  424.         MoveTo(centerPt.h, centerPt.v);
  425.         ClosePoly();
  426.         PaintPoly(scratchPoly);
  427.         KillPoly(scratchPoly);
  428.     }
  429. }
  430.  
  431.